/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦，本文采用木兰宽松许可证第2版]
 * 
 * https://zhiqim.org/project/zhiqim_kit/zhiqim_git.htm
 *
 * ZhiqimGit is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.git.http;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.zip.Deflater;

import org.zhiqim.git.Git;
import org.zhiqim.git.GitConstants;
import org.zhiqim.git.GitWalker;
import org.zhiqim.git.GitZiper;
import org.zhiqim.git.objer.ObjectLoader;
import org.zhiqim.git.objer.objectid.RevCommit;
import org.zhiqim.git.objer.objectid.RevObject;
import org.zhiqim.git.objer.objectid.RevTag;
import org.zhiqim.git.util.GitStreams;


/**
 * 上传包读写器
 *
 * @version v1.0.0 @author zouzhigang 2017-4-20 新建与整理
 */
public class UploadWriter implements GitConstants
{
    private final byte[] buf = new byte[16 * KiB];
    
    private final Git git;
    private final GitWalker walker;
    private final Deflater deflater;
    
    private UploadOutputStream packOut;
    private Progress progress;

    //需要传送的对象
    private List<RevObject> objectList = new ArrayList<>();
    
    public UploadWriter(Git git, GitWalker walker, Progress progress)
    {
        this.git =  git;
        this.walker = walker;
        this.progress = progress;
        
        //配置参数
        this.deflater = GitZiper.deflater(git.getConfig().getCompression());
    }
    
    /******************************************************************************/
    // 预处理
    /******************************************************************************/
    
    /**
     * 预处理，准备查询条件，查询到所有要输出的对象
     * 
     * @param wantRefobjList        需要的引用对象
     * @param wantCommitList        需要的提交对象
     * @throws IOException          异常
     */
    public void preparePack(List<RevObject> wantRefobjList, LinkedList<RevCommit> wantCommitList, Set<RevCommit> haveCommitList) throws IOException
    {
        progress.beginTask(PROGRESS_COUNTING_OBJECTS, Progress.UNKNOWN);
        
        //1.读出需要的引用对象增加到对象列表中，（如果是标签，找到对应的对象也一起加入）
        for (RevObject obj : wantRefobjList)
        {
            while (obj instanceof RevTag)
            {
                objectList.add(obj);
                obj = ((RevTag) obj).getObject();
                obj.parseHeaders(walker);
            }
            
            if (!(obj instanceof RevCommit))
                objectList.add(obj);
        }
        
        //2.读出从have到want中间的所有commit，（通过要求的提交对象列表向父提交循环，直到have）
        List<RevCommit> wantCommitAllList = walker.getWantCommitList(wantCommitList);
        
        for (RevCommit commit : wantCommitAllList)
        {
            objectList.add(commit);
            progress.update(1);
        }
        
        //3.读出从have到want的所有tree&blob，（排除have中有的tree&blob）
        List<RevObject> objList = new ArrayList<>();
        walker.list(objList, wantCommitAllList, haveCommitList);
        
        for (RevObject obj : objList)
        {
            objectList.add(obj);
            progress.update(1);
        }
        
        progress.endTask();
    }
    
    /******************************************************************************/
    // 写入包，写入文件头、对象和检验码
    /******************************************************************************/
    
    public void writePack(OutputStream packStream) throws IOException
    {
        packOut = new UploadOutputStream(packStream);
        
        //1.写入文件头
        GitStreams.writePackHeader(packOut, objectList.size());
        
        //2.依次写入对象
        writeObjects(objectList);
        
        //3.写入校验码
        packOut.write(packOut.getDigest());
    }
    
    /** 依次写入对象 */
    private void writeObjects(List<RevObject> list) throws IOException
    {
        for (RevObject object : list)
        {
            if (!object.isWriten())
                writeObject(object);
        }
    }

    /** 写入对象 */
    private void writeObject(RevObject object) throws IOException
    {
        packOut.resetCRC32();
        object.setWritten();

        ObjectLoader loader = git.getObjectLoader(object);
        byte[] data = loader.getData();
        GitStreams.writeObjectHeader(packOut, object.getType(), data.length);
        
        deflater.reset();
        deflater.setInput(data, 0, data.length);
        deflater.finish();
        
        do
        {
            int n = deflater.deflate(buf, 0, buf.length);
            if (n > 0)
                packOut.write(buf, 0, n);
        }
        while (!deflater.finished());
    }
}
